#include "dequeue.h"




/*this function allocates the needed mem. - don't forget to error-check allocations
 * input: nothing
 * output: null if something went wrong or a pointer to the allocated AND INITIALIZED struct
 */
dequeue *createDequeue(){
	dequeue * dq= NULL;

	dq = (dequeue *) malloc(sizeof(dequeue));
	if (dq == NULL){
		perror("dequeue: bad mem alloc");
		return NULL;
	}

	dq->DEQlock = PTHREAD_MUTEX_INITIALIZER;
	(*dq).top = NULL;
	(*dq).bottom = NULL;
	return dq;
}

/*this function frees all the mem. allocated - you should check that everything inside the struct
 * you allocated is freed to (recursive free - yes i used the word recursive)
 *
 * input: pointer to a dequeue struct
 * output: nothing
 */
void freeDequeue(dequeue *dq){
	link * temp=NULL;	

	pthread_mutex_lock( (*dq).DEQlock );
	
	if ((*dq).top != NULL){
		while ((*dq).top != NULL){
			temp= (*dq).top;
			free((*(*dq).top).value);
			(*dq).top = (*(*dq).top).down;
			(*(*dq).top).up = NULL;
			free(temp);
			if ((*dq).top == NULL)
				(*dq).bottom = NULL;
		}
	}
	pthread_mutex_unlock( (*dq).DEQlock );
	pthread_mutex_destroy( (*dq).DEQlock );
	free(dq);
	return;

}

/*a function to add a new string to the top of the dequeue
 * input: a pointer to  dequeue, a pointer to the string to add
 * output: on any mem. allocation failure return -1
 * 		   on any generic failure return 0 (if this case even exists)
 * 			on success return 1
 */
int push(dequeue *dq, char *str){

	link * node = NULL;
	char * val = NULL;



	node = (link *) malloc(sizeof(link));
	if (node == NULL){
		perror("dequeue: bad mem alloc");
		return -1;
	}
	val = (char *) malloc(strlen(str)+1);
	if (val == NULL){
		perror("dequeue: bad mem alloc");
		free(node);
		return -1;
	}
	(*node).value = val;
	(*node).up = NULL;
	(*node).down = NULL;
	strcpy((*node).value, str); 

	pthread_mutex_lock( (*dq).DEQlock );

	if ((*dq).top==NULL){
		(*dq).top = node;
		(*dq).bottom = node;
	}else{


		(*node).down = (*dq).top;
		(*(*dq).top).up = node;

		(*dq).top = node;
	}
	pthread_mutex_unlock( (*dq).DEQlock );
	return 1;
}

/*this function removes the top member of the dequeue from the struc
 * input: a pointer to dequeue
 * output: return 1 on success, 0 on failue
 */
int pop(dequeue *dq){
	link * temp;

	pthread_mutex_lock( (*dq).DEQlock );

	if ((*dq).top == NULL){
		pthread_mutex_unlock( (*dq).DEQlock );
		perror("dequeue: cannot top an empty dequeue");
		return 0;
	}else{

		temp= (*dq).top;
		free((*(*dq).top).value);
		(*dq).top = (*(*dq).top).down;
		(*(*dq).top).up = NULL;
		free(temp);

		if ((*dq).top == NULL)
			(*dq).bottom = NULL;
			
		pthread_mutex_unlock( (*dq).DEQlock );
		return 1;
	}
}

/*this function returns the string we hold at the top of the dequeue to the buffer given
 * the bufferSize is used to see if the buffer can hold the string at the top
 * and to see we don't get any seg-faults!
 * input: a pointer to a dequeue, a string buffer and the buffer's size
 * output: return 1 on success, 0 on failure
 */
int top(dequeue *dq, char *buffer, int bufferSize){
	int i;
	char * s;

	pthread_mutex_lock( (*dq).DEQlock );
	if ((*dq).top == NULL){
		pthread_mutex_unlock( (*dq).DEQlock );
		perror("dequeue: cannot top an empty dequeue");
		return 0;
	}
	else{
		s= (*(*dq).top).value;
		pthread_mutex_unlock( (*dq).DEQlock );
		for (i=0; (s[i] != '\0') ;){

			if (i >= bufferSize-1){
				perror("dequeue: buffer is too small");
				buffer[bufferSize-1]='\0';
				return 0;
			}else{
				buffer[i]= s[i];
			}
			i++;
		}
		buffer[i]='\0';
		return 1;
	}
}


/*add here the rest of the interface for the rest of the functions we agreed on
 * and send it back to me - i'll keep working on the program usin only stack command
 * and the struct from this interface so we can work  - we don't realy have the time!
 */


/*
check if a DEQ is empty. 1=empty , 0=not.
 */
int empty(dequeue *dq){
	pthread_mutex_lock( (*dq).DEQlock );
	if (((*dq).top == NULL) && ((*dq).bottom == NULL)){
		pthread_mutex_unlock( (*dq).DEQlock );
		return 1;
	}else{
		pthread_mutex_unlock( (*dq).DEQlock );
		return 0;
	}
	
}


int pushBottom(dequeue *dq, char *str){
	link * node = NULL;
	char * val = NULL;

	node = (link *) malloc(sizeof(link));
	if (node == NULL){
		perror("dequeue: bad mem alloc");
		return -1;
	}
	val = (char *) malloc(strlen(str)+1);
	if (val == NULL){
		perror("dequeue: bad mem alloc");
		free(node);
		return -1;
	}
	(*node).value = val;
	(*node).up = NULL;
	(*node).down = NULL;
	strcpy((*node).value, str); 

	pthread_mutex_lock( (*dq).DEQlock );

	if ((*dq).bottom==NULL){
		(*dq).top = node;
		(*dq).bottom = node;
	}else{
		(*node).up = (*dq).bottom;
		(*(*dq).bottom).down = node;
		(*dq).bottom = node;
	}
	pthread_mutex_unlock( (*dq).DEQlock );
	return 1;
}

/*this function removes the top member of the dequeue from the struc
 * input: a pointer to dequeue
 * output: return 1 on success, 0 on failue
 */
int popBottom(dequeue *dq){
	link * temp;

	pthread_mutex_lock( (*dq).DEQlock );	

	if ((*dq).bottom == NULL){
		pthread_mutex_unlock( (*dq).DEQlock );
		perror("dequeue: cannot top an empty dequeue");
		return 0;
	}else{
		temp= (*dq).bottom;
		free((*(*dq).bottom).value);
		(*dq).bottom = (*(*dq).bottom).up;
		(*(*dq).bottom).down = NULL;
		free(temp);

		if ((*dq).bottom == NULL)
			(*dq).top = NULL;
		pthread_mutex_unlock( (*dq).DEQlock );
		return 1;
	}
}

int bottom(dequeue *dq, char *buffer, int bufferSize){
	int i;
	char * s;

	pthread_mutex_lock( (*dq).DEQlock );	
	if ((*dq).bottom == NULL){
		pthread_mutex_unlock( (*dq).DEQlock );
		perror("dequeue: cannot top an empty dequeue");
		return 0;
	}else{
		s= (*(*dq).bottom).value;
		pthread_mutex_unlock( (*dq).DEQlock );
		for (i=0; (s[i] != '\0') ;){

			if (i >= bufferSize-1){
				perror("dequeue: buffer is too small");
				buffer[bufferSize-1]='\0';
				return 0;
			}else{
				buffer[i]= s[i];
			}
			i++;
		}
		buffer[i]='\0';
		return 1;
	}
}
